---
title: Built-in Search
description: Built-in document search of Fumadocs
---

Fumadocs supports searching document based on Orama.

As the built-in search of Fumadocs, It is the default but also recommended
option since it's easier to setup and totally free.

> If you're using a CMS, you may use the API provided by the CMS instead.

## Setup

Returns a detailed result with matching headings and contents.

It accepts structured data processed from a markdown/MDX document, and index it with
Orama. You can extract the structured data using the [Structure](/docs/headless/mdx/structure) remark plugin.

It cannot extract content from rehype plugin generated content (remark plugins are supported).

### From Source

Create a route handler from Source API.

```json doc-gen:file
{
  "file": "../../examples/next-mdx/app/api/search/route.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"app/api/search/route.ts\""
  }
}
```

### From Search Indexes

Pass search indexes to the function, see type information for required properties.

```json doc-gen:file
{
  "file": "../../examples/next-mdx/app/api/search/route-full.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"app/api/search/route.ts\""
  }
}
```

### Client

You can query it using:

- **Fumadocs UI**: use the [built-in Search UI](/docs/ui/search).
- **Search Client**:

  ```ts twoslash
  import { useDocsSearch } from 'fumadocs-core/search/client';

  const client = useDocsSearch({
    type: 'fetch',
  });
  ```

### Tag Filter

Support filtering by tag, it's useful for implementing multi-docs similar to this documentation.

```json doc-gen:file
{
  "file": "../../examples/next-mdx/app/api/search/route-tag.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"app/api/search/route.ts\""
  }
}
```

and update your search client:

- **Fumadocs UI**:
  Configure [Tag Filter](/docs/ui/search#tag-filter) on Search UI.
- **Search Client**:
  pass a tag to the hook.

  ```ts
  import { useDocsSearch } from 'fumadocs-core/search/client';

  // Pass `tag` in your custom search dialog
  const client = useDocsSearch(
    {
      type: 'fetch',
    },
    undefined, // locale code, can be `undefined`
    'tag',
  );
  ```

### Index by Content

Index with the raw content of document (unrecommended).

```ts title="app/api/search/route.ts"
import { allDocs } from 'content-collections';
import { createSearchAPI } from 'fumadocs-core/search/server';

export const { GET } = createSearchAPI('simple', {
  indexes: allDocs.map((docs) => ({
    title: docs.title,
    content: docs.content, // Raw Content
    url: docs.url,
  })),
});
```

## Internationalization

<div className='steps [&_h4]:step'>

<h4>Update the route handler (From Source)</h4>

You don't need to change anything, it handles i18n automatically.

<h4>Update the route handler (From Search Indexes)</h4>

Use `createI18nSearchAPI` for i18n functionality.

```json doc-gen:file
{
  "file": "../../examples/i18n/app/api/search/route-full.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"app/api/search/route.ts\""
  }
}
```

<h4>Update search client</h4>

<Callout type="info" title="For Fumadocs UI">
  You can ignore this, Fumadocs UI handles this when you have i18n configured
  correctly.
</Callout>

Add `locale` to the search client, this will only allow pages with specified locale to be searchable by the user.

```ts
const { search, setSearch, query } = useDocsSearch(
  {
    type: 'fetch',
  },
  locale,
);
```

</div>

### Special Languages

Orama requires extra configurations for Chinese, Japanese, and some other languages.

```json doc-gen:file
{
  "file": "../../examples/i18n/app/api/search/route.ts",
  "codeblock": {
    "lang": "ts",
    "meta": "title=\"app/api/search/route.ts\""
  }
}
```

See [Orama Docs](https://docs.orama.com/open-source/supported-languages/using-chinese-with-orama) for more details.

## Static Export

To work with Next.js static export, use `staticGET` from search server.

```ts title="app/api/search/route.ts"
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';

// it should be cached forever
export const revalidate = false;

export const { staticGET: GET } = createFromSource(source);
```

> `staticGET` is also available on `createSearchAPI`.

and update your search clients:

- **Fumadocs UI**:

  You can pass search options to Root Provider. See [`options`](/docs/ui/search#search-options).

- **Search Client**:

  On your search client, use `static` instead of `fetch`.

  ```ts
  import { useDocsSearch } from 'fumadocs-core/search/client';

  const client = useDocsSearch({
    type: 'static',
  });
  ```

<Callout type='warn' title="Be Careful">

    Static Search requires clients to download the exported search indexes.
    For large docs sites, its size can be really big.

    Especially with i18n (e.g. Chinese tokenizers), the bundle size of tokenizers can exceed ~500MB.
    You should use 3rd party solutions like Algolia for these cases.

</Callout>

## Search Client

`useDocsSearch` is a hook to search documents using the built-in search clients.

### Client

Choose the client to use.

```ts
import { useDocsSearch } from 'fumadocs-core/search/client';

const client = useDocsSearch({
  type: 'static',
  // you can pass options for the client you specified
});
```

| Type              | Description                                                      |
| ----------------- | ---------------------------------------------------------------- |
| `fetch` (default) | send HTTP request to a search server with `fetch`                |
| `static`          | Load search map on client-side and query them                    |
| `algolia`         | Use Algolia Search (See [Algolia](/docs/headless/search/algolia) |

### Return Type

| Prop        | Type                  | Description                    |
| ----------- | --------------------- | ------------------------------ |
| `query`     | `Query`               | Query status and results       |
| `search`    | `string`              | Searching text (not debounced) |
| `setSearch` | `(v: string) => void` | Set searching text             |

### Response Data

| Type             |                                                 |
| ---------------- | ----------------------------------------------- |
| `empty`          | If the searching text is empty or blank         |
| `SortedResult[]` | Array of matching pages, headings and contents. |

### Custom Algorithm

You can port your own search algorithm by returning a list of `SortedResult`
from your custom `/api/search/route.ts` route handler (API Endpoint). You can also integrate it
with Fumadocs UI.

<AutoTypeTable path="./content/docs/headless/props.ts" name="SortedResult" />

## Headless

You can host the search server on Express or Elysia, without Next.js.

```ts
import { initAdvancedSearch } from 'fumadocs-core/search/server';

const server = initAdvancedSearch({
  // options
});

server.search('query', {
  // you can specify `locale` and `tag` here
});
```
